using AutoMapper;
using Common;
using Front_Efcore.Entity.subarea;
using Frontdatabase;
using Frontdatabase.Entity.Video;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Model;
using Model.UpVideoModel;
using Newtonsoft.Json;
using StackExchange.Redis;
using System.Drawing;
using System.IO;
using System.Security.Cryptography;


namespace WebFront.Service.Impl
{
    /// <summary>
    /// 上传投稿视频接口实现类
    /// </summary>
    public class UpVideoServiceImpl
        (IConfiguration _configuration, FrontDbContext _context, RedisHelper _client,
        IMapper _mapper
        )
        : IUpVideoService
    {
        private readonly IDatabase _redis = _client.GetDatabase();

        public async Task<ResResult> CategoryAndSubarea()
        {
            var list = await _context.Categories
                                     .Select(ca => new
                                     {
                                         ca.Id,
                                         ca.Name,
                                         Subareas = ca.Subareas.Select(su => new
                                         {
                                             su.Id,
                                             su.Name,
                                             su.Synopsis
                                         })
                                     }
                                     )
                                     .ToListAsync();
            return new ResResult(list);
        }

        public async Task<ResResult> UploadFrom(UploadVideoForm form)
        {



            var formMd5 = UpvideofromMD5(form);
            //存入redis中
            _ = await _redis.HashSetAsync(formMd5, Videofrom, SerializeHelper.Serialize(form));


            return new ResResult(new
            {
                md5 = formMd5,
                DateTime = DateTime.Now,
            });
        }

        public async Task<ResResult> UploadCoverChunkSave(UpVideoCoverChunkForm videoCoverChunk, IFormFile cover)
        {
            //设置视频封面切片存储路径
            var path = Path.Combine(ImgChunkPath, videoCoverChunk.Md5 + Path.GetExtension(cover.FileName));
            //如果视频封面切片不存在
            var exit = Path.Exists(path);
            if (!exit)
            {
                using var stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write);
                //保存封面切片到服务器本地
                await cover.CopyToAsync(stream);
            }

            //保存到redis
            _ = await _redis.HashSetAsync(videoCoverChunk.FormMd5, CoverChunkIndex(videoCoverChunk.Index), path);
            return new ResResult(new
            {
                videoCoverChunk,
                isExists = exit
            });
        }

        public async Task<ResResult> UploadMerginCoverSave(UpVideoMerginCoverForm _form)
        {
            var redisValueDic = new Dictionary<int, Task<RedisValue>>();
            //批量读取redis 

            var batch = _redis.CreateBatch();

            for (int i = 1; i <= _form.ChunkCount; i++)
            {
                var redisValue = batch.HashGetAsync(_form.FormMd5, CoverChunkIndex(i));
                redisValueDic.Add(i, redisValue);
            }
            batch.Execute();

            //取出封面切片 依照顺序写入文件流中 生成封面图
            var coverPath = Path.Combine(VideoListCoverPath, _form.Md5 + _form.PicType);
            if (!File.Exists(coverPath))
            {
                using var fws = new FileStream(coverPath, FileMode.CreateNew, FileAccess.Write);
                for (int i = 1; i <= _form.ChunkCount; i++)
                {
                    var path = (await redisValueDic[i]).ToString();
                    using var frs = new FileStream(path, FileMode.Open, FileAccess.Read);
                    while (true)
                    {
                        byte[] buffer = new byte[1024];
                        int r = frs.Read(buffer, 0, buffer.Length);
                        if (r == 0) break;
                        fws.Write(buffer, 0, r);
                    }
                    //删除切片文件
                    frs.Close();
                    File.Delete(path);
                }


                var resBoolList = new List<Task<bool>>();
                //删除redis中封面切片数据
                batch = _redis.CreateBatch();
                for (int i = 1; i <= _form.ChunkCount; i++)
                {
                    var resbool = batch.HashDeleteAsync(_form.FormMd5, CoverChunkIndex(i));
                    resBoolList.Add(resbool);
                }
                batch.Execute();
                resBoolList.ForEach(async res => await res);
            }
            //保存封面地址在redis中
            _ = await _redis.HashSetAsync(_form.FormMd5, CoverPath, coverPath);

            return new ResResult(new
            {
                coverPath,
                _form.FormMd5,
                _form.Md5
            });
        }

        public async Task<ResResult> UploadVideoChunkSave(IFormFile VideoChunk, UploadVideoFileChunkForm _form)
        {

            var path = Path.Combine(VideoChunkPath, _form.Md5 + Path.GetExtension(VideoChunk.FileName));
            var ext = Path.Exists(path);
            if (!ext)
            {
                using var stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write);
                //保存到服务器本地
                await VideoChunk.CopyToAsync(stream);

            }
            //保存到redis
            _ = await _redis.HashSetAsync(_form.FormMd5, VideoChunkIndex(_form.Index, _form.VideoMd5), path);

            return new ResResult(new
            {
                _form,
                DateTime.Now,
                Size = VideoChunk.Length
            });
        }

        public async Task<ResResult> UploadMerginVideoSave(UploadVideoMergeform _form)
        {
            var redisValueDic = new Dictionary<int, Task<RedisValue>>();
            //批量读取redis 

            var batch = _redis.CreateBatch();
            for (int i = 1; i <= _form.ChunkCount; i++)
            {
                var redisValue = batch.HashGetAsync(_form.FormMd5, VideoChunkIndex(i, _form.Md5));

                redisValueDic.Add(i, redisValue);
            }
            batch.Execute();

            //取出视频切片 依照顺序写入文件流中 生成视频
            var videoPath = Path.Combine(VideoListFilePath, _form.Md5 + _form.VideoType);
            if (!Path.Exists(videoPath))
            {
                using var fws = new FileStream(videoPath, FileMode.CreateNew, FileAccess.Write);
                for (int i = 1; i <= _form.ChunkCount; i++)
                {
                    var path = (await redisValueDic[i]).ToString();
                    using var frs = new FileStream(path, FileMode.Open, FileAccess.Read);
                    while (true)
                    {
                        byte[] buffer = new byte[1024];
                        int r = frs.Read(buffer, 0, buffer.Length);
                        if (r == 0) break;
                        fws.Write(buffer, 0, r);
                    }
                    //删除切片文件
                    frs.Close();
                    File.Delete(path);


                }

                //删除redis中视频切片数据
                var resBoolList = new List<Task<bool>>();
                batch = _redis.CreateBatch();
                for (int i = 1; i <= _form.ChunkCount; i++)
                {
                    var resbool = batch.HashDeleteAsync(_form.FormMd5, VideoChunkIndex(i, _form.Md5));
                    resBoolList.Add(resbool);
                }
                batch.Execute();
                resBoolList.ForEach(async res => await res);
            }
            _form.VideoPath = videoPath;
            //存储表单对象在redis中
            var json = JsonConvert.SerializeObject(_form);
            _ = await _redis.HashSetAsync(_form.FormMd5, VideoPath(_form.Md5), json);
            return new ResResult(new
            {
                DateTime.Now,
                _form.Md5,
                _form.FormMd5,

            });
        }
        #region 变量 方法
        /// <summary>
        /// 获取表单生成的MD5值
        /// </summary>
        /// <param name="form"></param>
        /// <returns></returns>
        private static string UpvideofromMD5(UploadVideoForm form) => "upvideofrom_" + MD5Helper.ToMd5Str(JsonConvert.SerializeObject(form));
        private readonly static string Videofrom = "from";
        /// <summary>
        /// 本地文件存储路径
        /// </summary>
        private string StoredFilesPath => _configuration?.GetSection("FilePath")?.GetSection("StoredFilesPath")?.Value ?? string.Empty;
        /// <summary>
        /// 本地文件(图片切片)存储路径
        /// </summary>
        private string ImgChunkPath => StoredFilesPath + "\\img\\ImgChunk";
        /// <summary>
        /// 本地文件(图片切片)存储路径
        /// </summary>
        private string VideoChunkPath => StoredFilesPath + "\\video\\VideoChunk";
        /// <summary>
        /// 本地投稿视频封面存储路径
        /// </summary>
        private string VideoListCoverPath => StoredFilesPath + "\\img\\VideoListCover";
        /// <summary>
        /// 本地投稿视频存储路径
        /// </summary>
        private string VideoListFilePath => StoredFilesPath + "\\video\\VideoListVideo";
        /// <summary>
        /// 封面切片在redis中的key
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        private string CoverChunkIndex(int index) => "cover:" + index;
        /// <summary>
        /// 视频切片在redis中的key
        /// </summary>
        /// <param name="index"></param>
        /// <param name="videoMd5">视频切片所属影片的md5</param>
        /// <returns></returns>
        private string VideoChunkIndex(int index, string videoMd5) => "video:" + index + "_" + videoMd5;
        /// <summary>
        /// 视频封面地址在redis中的key
        /// </summary>
        private static string CoverPath => "CoverPath";
        /// <summary>
        /// 视频对象在MD5中的key
        /// 
        /// </summary>
        /// <param name="VideoMd5"></param>
        /// <returns></returns>
        private string VideoPath(string VideoMd5) => $"Video_{VideoMd5}" ;
        #endregion

    }
}
